
package jason.util;

import java.io.*;
import java.net.URL;
import java.util.*;

/**
 * Utility class holding a list of utility methods used at many classes.
 */
public class Util {
	/**
	 * Strips the backslashes in the input string. For example:
	 * <ul>
	 * <li>\" -> ",</li>
	 * <li>\n -> &lt;new line&gt;,</li>
	 * <li>\48 -> 0</li>
	 * </ul>
	 */
	public static String stripslashes(String input) {
		StringBuilder str = new StringBuilder();
		boolean escaping = false;
		int num = 0; // a character specified as an octal number
		int pos = 0; // the maximum allowed characters
		boolean numing = false;
		for(char c : input.toCharArray()) {
			boolean normal = false;
			
			if (numing) {
				if ('0' <= c && c <= '9') {
					num *= 8;
					num += (c - '0');
					
					if (--pos == 0) {
						str.append((char)num);
						numing = false;
						escaping = false;
					}
				} else {
					str.append((char)num);
					numing = false;
					escaping = false;
					
					normal = true;
				}
			} else if (escaping) {
				if ('0' <= c && c <= '9') {
					numing = true;
					num = (c - '0');
					if (c <= '3') {
						pos = 2; // 2 more digits: 377
					} else {
						pos = 1; // 1 more digit: 40-99
					}
				} else {
					switch (c) {
						case 'n':
							str.append('\n');
							break;
						case 't':
							str.append('\t');
							break;
						case 'b':
							str.append('\b');
							break;
						case 'r':
							str.append('\r');
							break;
						case 'f':
							str.append('\f');
							break;
						case '\\':
						case '\'':
						case '"':
							str.append(c);
							break;
						default: // should be caught by the parser
							str.append('\\');
							str.append(c);
							break;
					}
					escaping = false;
				}
			} else {
				normal = true;
			}
			
			if (normal) {
				if (c == '\\') {
					escaping = true;
				} else {
					str.append(c);
				}
			}
		}
		
		if (numing) {
			str.append((char)num);
		} else if (escaping) {
			str.append('\\');
		}
		
		return str.toString();
	}
	
	public static String getAbsPath(String path) {
		try {
			URL url = new URL(path);
			if (url.getProtocol().equalsIgnoreCase("file")) {
				path = url.toURI().normalize().getPath();
				throw new Exception();
			}
			return url.toURI().normalize().toString();
		} catch (Exception e) {
			File f = new File(path);
			try {
				return f.getCanonicalPath();
			} catch (IOException e2) {
				return f.getAbsolutePath();
			}
		}
	}
	
	/** Object wrapping for a single object. */
	public static final class Obj<T> {
		public T o;
		
		public Obj(T o) {
			this.o = o;
		}
		
		public boolean equals(Object obj) {
			if (obj instanceof Obj<?>) {
				Obj<?> ob = (Obj<?>)obj;
				return safeEq(o, ob.o);
			}
			return false;
		}
		
		public final String toString() {
			return o.toString();
		}
		
		public int hashCode() {
			return safeHashCode(o);
		}
	}
	
	/** Object wrapping for two objects. */
	public static final class Pair<F, S> {
		public F fst;
		public S snd;
		
		public Pair(F fst, S snd) {
			this.fst = fst;
			this.snd = snd;
		}
		
		public boolean equals(Object obj) {
			if (obj instanceof Pair) {
				Pair o = (Pair)obj;
				return safeEq(fst, o.fst) && safeEq(snd, o.snd);
			}
			return false;
		}
		
		public final String toString() {
			return "(" + fst + ", " + snd + ")";
		}
		
		public int hashCode() {
			int hashCode = safeHashCode(fst);
			hashCode = hashCode * 29 + safeHashCode(snd);
			return hashCode;
		}
	}
	
	/** Object wrapping for three objects. */
	public static final class Triple<F, S, T> {
		public F fst;
		public S snd;
		public T trd;
		
		public Triple(F fst, S snd, T trd) {
			this.fst = fst;
			this.snd = snd;
			this.trd = trd;
		}
		
		public boolean equals(Object obj) {
			if (obj instanceof Triple) {
				Triple o = (Triple)obj;
				return safeEq(fst, o.fst) && safeEq(snd, o.snd) && safeEq(trd, o.trd);
			}
			return false;
		}
		
		public int hashCode() {
			int hashCode = safeHashCode(fst);
			hashCode = hashCode * 29 + safeHashCode(snd);
			hashCode = hashCode * 29 + safeHashCode(trd);
			return hashCode;
		}
		
		public final String toString() {
			//return "(" + fst + ", " + snd + ", " + trd + ")";
			return "(\"" + fst + "\", " + snd + ", \"" + trd + "\")\n";
		}
	}
	
	/** Equality check without null pointer exception. */
	public static final boolean safeEq(Object o1, Object o2) {
		return (o1 == null ? o2 == null : o1.equals(o2));
	}
	
	/** Hash code without null pointer exception. */
	public static final int safeHashCode(Object o) {
		if (o == null) return 0;
		return o.hashCode();
	}
	
	/** Replace the first object found in the list which equals to obj with the object obj. */
	public static final <T> void listReplace(List<T> list, T obj) {
		int i = list.indexOf(obj);
		if (i < -1) {
			list.add(obj);
			return;
		}
		list.remove(i);
		list.add(i, obj);
	}
}
